#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h>
#include <pthread.h>
#include <android/log.h>  
#include <sys/time.h>

#include "rtsp_client.h" 
#include "h264_disp.h"
#include "PreviewRenderer.h"

#ifndef MAX_PATH
#define MAX_PATH 256
#endif
#define LOG_TAG "egv-jni"

#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)

static JavaVM* sVM= NULL;
static jclass sClazz = NULL;


using namespace android;

JavaVM *m_vm;
static jmethodID m_recvframeCallbackID = NULL;
static int is_running = 0;

JNIEXPORT jint JNICALL FpvJni_Init(JNIEnv *env, jobject clazz, jstring ipaddr)
{
	ALOGD("Enters %s\n", __func__);
	const char* cIpAddr = NULL;
	int i = 0;

	if(is_running )
	{
		return 0;
	}
	cIpAddr = env->GetStringUTFChars(ipaddr, NULL);	
	if (cIpAddr == NULL) 
	{ 		
		return -1;	
	}
	//if(NetCmd_Init(cIpAddr) == NCRET_OK)
	{
		is_running = 1;
		//pthread_create(&get_status_thread_id,NULL,getStatusThread,NULL);
	}
	return 0;
}


JNIEXPORT jint JNICALL FpvJni_DeInit(JNIEnv *env, jobject clazz)
{
	ALOGD("Enter %s\n", __func__);
	is_running = 0;
	usleep(10000);
	//pthread_join(get_status_thread_id, NULL);
	//NetCmd_Uninit();
	return 0;
}

bool is_i_frame(unsigned char *tmp)
{
	if(tmp[0] == 0x00 &&
		tmp[1] == 0x00 &&
		tmp[2] == 0x00 &&
		tmp[3] == 0x01 &&
		tmp[4] == 0x67)
	{
		ALOGD("Found i frame");
		return 1;
	}
	else if(tmp[0] == 0x00 &&
		tmp[1] == 0x00 &&
		tmp[2] == 0x00 &&
		tmp[3] == 0x01 &&
		tmp[4] == 0x09 &&
		tmp[5] == 0x10 &&
		tmp[6] == 0x00 &&
		tmp[7] == 0x00 &&
		tmp[8] == 0x00 &&
		tmp[9] == 0x01 &&
		tmp[10] == 0x67)
	{
		ALOGD("Found i frame");
		return 1;
	}
	else 
	{
		ALOGD("Skip frame");
	//	print_hex(tmp, 12);
		return 0;
	}
	return 0;
 }
 
 static long get_tick_count()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (tv.tv_sec * 1000 + tv.tv_usec/1000);
}

static void test_fps_bitrate(int frame_size)
{
    static int frames_num =0;
    static long start_time = 1;
    static long total_frame_size = 0;
    if(start_time == 1)
    {
        start_time = get_tick_count();
    }
    frames_num++;
    total_frame_size += frame_size;
    if(frames_num%30 == 0)
    {
        ALOGD("fps = %ld\n", (long)(frames_num*1000/(get_tick_count()-start_time)));
        ALOGD("birtate = %ld\n", (long)(total_frame_size*1000/(get_tick_count()-start_time)));
        start_time = get_tick_count();
        frames_num = 0;
        total_frame_size = 0;
    }
}


 static FILE *fp_dump = NULL;
static int meet_i_frame = 0;
static PreviewRenderer *previewRender= NULL;
static unsigned char* pYUVBuffer = NULL;
int preview_running = 0;

static int  receive_frame(void* pFrameBuffer, int nBufferLen, unsigned long long time)
{
	
	//ALOGD("Enter %s:%d\n", __func__, __LINE__);
	int i;
	unsigned char *phead = (unsigned char*)pFrameBuffer;
	
	//test_fps_bitrate(nBufferLen);
	//if(preview_running==1)
	{
		if(0==meet_i_frame)
		{
			if(is_i_frame((unsigned char*) pFrameBuffer))
			{
				meet_i_frame=1;
			}
			else
			{
				return 0;
			}
		}
		//h264disp_show(pFrameBuffer, nBufferLen, 0);
		ALOGD("nBufferLen = %d", nBufferLen);
		
		jbyte fill[nBufferLen];
        for (i = 0; i < nBufferLen; i++)
        {
            fill[i] = phead[i];
        }
		
		
		// attach to the JavaVM thread and get a JNI interface pointer
        JNIEnv *env = NULL;
        sVM->AttachCurrentThread ( (JNIEnv **) &env, NULL);
		if(env == NULL)
		{
			ALOGE("%s: Error in line:%d", __FUNCTION__, __LINE__);   
			return NULL;
		}	
		
        // create java int array
        jbyteArray byteArray = env->NewByteArray (nBufferLen);

        // populate java int array with fill data
        env->SetByteArrayRegion (byteArray, 0, nBufferLen, fill);

        // call java method and pass amplitude array
        env->CallStaticVoidMethod(sClazz, m_recvframeCallbackID, byteArray);

        // detach from the JavaVM thread
        sVM->DetachCurrentThread();
		
	}
	return  0;
}


JNIEXPORT jint FpvJni_StartPreview(JNIEnv *env, jobject clazz,jobject jsurface,jstring ipaddr)
{
	//if (jsurface) 
	//{
	if (NULL == ipaddr) 
	{
		return -1;
	}
	
	const char* cIpAddr = NULL;
	char strUrl[128] = {0};	
	//将java层的地址ipaddr转换成C层的地址cDirString
	cIpAddr = env->GetStringUTFChars(ipaddr, NULL);
	if (cIpAddr == NULL) { 
		return -1;
	}
	
	meet_i_frame = 0;

	{
		//球相机
		//sprintf(strUrl, "rtsp://%s/live1.sdp", cIpAddr);
		//2K相机
		sprintf(strUrl, "rtsp://%s/live1.sdp", cIpAddr);
		//小蚁相机
		//sprintf(strUrl, "rtsp://%s/ch0_0.h264", cIpAddr);
		//运行在linux上的模拟器
		//sprintf(strUrl, "rtsp://%s:8554/test2.264", cIpAddr);
		//h264disp_init(env, jsurface);    
		RtspClient_StartRecv(strUrl, receive_frame);
		//当前连接的设备是球相机时调用
 
	}
	preview_running =1;

	return 0;
}


JNIEXPORT jint FpvJni_StopPreview(JNIEnv *env, jobject clazz)
{
	if(preview_running==1)
	{
		RtspClient_StopRecv();
		meet_i_frame = 0;
		preview_running  =0;
		
		//h264disp_uninit();
		
		if(fp_dump)
		{
			fclose(fp_dump);
			fp_dump = NULL;
		}
		
		if(NULL != pYUVBuffer)
		{
			free(pYUVBuffer);
			pYUVBuffer = NULL;
		}
		if(previewRender != NULL)
		{
			delete previewRender;
			previewRender = NULL;
		}
	}
	return 0;
}

static JNINativeMethod methods[] = {
    { "FpvJni_Init"   		, "(Ljava/lang/String;)I"        	, (void*)FpvJni_Init },
    { "FpvJni_DeInit"       , "()I"       	, (void*)FpvJni_DeInit},
	
	
	{"FpvJni_StartPreview",				"(Landroid/view/Surface;Ljava/lang/String;)I",		(void*)FpvJni_StartPreview},
	{"FpvJni_StopPreview",				"()I",			(void*)FpvJni_StopPreview},

};

static const char *classPathName = "com/ldm/rtsp/sunny/FpvJni";

/*
 * Register several native methods for one class.
 */
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL) {
        ALOGE("Native registration unable to find class '%s'", className);
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        ALOGE("RegisterNatives failed for '%s'", className);
        return JNI_FALSE;
    }
	
	m_recvframeCallbackID = env->GetStaticMethodID(clazz, "callBackRecvFrame", "([B)V");
    if (m_recvframeCallbackID == NULL) {
        ALOGE("Can't find m_recvframeCallbackID");
	} 	

	/*
	method_ReportStatus = env->GetStaticMethodID(clazz, "callBackReportStatus", "(IIIIIIIIIIIIIIIIIIJIIILjava/lang/String;)V");
    if (method_ReportStatus == NULL) {
        ALOGE("Can't find method_ReportStatus");
	}  */	
	sClazz = (jclass)env->NewGlobalRef(clazz);
    return JNI_TRUE;
}

/*
 * Register native methods for all classes we know about.
 *
 * returns JNI_TRUE on success.
 */
static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }

  return JNI_TRUE;
}

// ----------------------------------------------------------------------------

/*
 * This is called by the VM when the shared library is first loaded.
 */
 
typedef union {
    JNIEnv* env;
    void* venv;
} UnionJNIEnvToVoid;

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    UnionJNIEnvToVoid uenv;
    uenv.venv = NULL;
    jint result = -1;
    JNIEnv* env = NULL;
    
    ALOGE("JNI_OnLoad");

    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("ERROR: GetEnv failed");
        goto bail;
    }
    env = uenv.env;
	sVM = vm;
    if (registerNatives(env) != JNI_TRUE) {
       ALOGE("ERROR: registerNatives failed");
        goto bail;
    }    
	
    result = JNI_VERSION_1_4;    
bail:
    return result;
}
